package com.CodeCraft.builder;

import com.CodeCraft.bean.Constants;
import com.CodeCraft.bean.FiledInfo;
import com.CodeCraft.bean.TableInfo;
import com.CodeCraft.utils.JsonUtils;
import com.CodeCraft.utils.PropertiesUtils;
import com.CodeCraft.utils.StringUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.sql.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * <p>
 * 描述: 数据库表结构读取工具类，用于获取数据库中的表信息
 * </p>
 *
 * @author Dorain
 * @version 1.0.0
 * @since 2025/3/30
 */
public class BuildTable {
    // 日志记录器，用于输出日志信息
    private static final Logger logger = LoggerFactory.getLogger(BuildTable.class);
    // SQL语句：查询表状态信息（包含表名、注释等）
    private static final String SQL_SHOW_TABLE_STATUS = "show table status";
    private static final String SQL_SHOW_TABLE_FIELDS = "show full fields from %s";
    private static final String SQL_SHOW_TABLE_INDEX = "show index  from %s";
    // 数据库连接对象
    private static Connection connection = null;

    // 静态代码块：在类加载时初始化数据库连接
    static {
        // 从配置文件读取数据库连接参数
        String driverName = PropertiesUtils.getProperty("db.driverClassName");
        String url = PropertiesUtils.getProperty("db.url");
        String username = PropertiesUtils.getProperty("db.username");
        String password = PropertiesUtils.getProperty("db.password");

        try {
            // 1. 加载数据库驱动类
            Class.forName(driverName);
            // 2. 建立数据库连接
            connection = DriverManager.getConnection(url, username, password);
        } catch (ClassNotFoundException e) {
            // 处理驱动类未找到异常
            e.printStackTrace();
            logger.error("数据库驱动加载失败: {}", e.getMessage());
        } catch (SQLException e) {
            // 处理数据库连接异常
            e.printStackTrace();
            logger.error("数据库连接失败: {}", e.getMessage());
        }
    }

    /**
     * 对外公开方法：获取数据库中的所有表信息
     */
    public static List<TableInfo> getTables() {
        PreparedStatement ps = null;       // SQL语句对象
        ResultSet tableResult = null;      // 结果集对象

        List<TableInfo> tableInfoList = new ArrayList<>();
        try {
            // 1. 创建预处理语句对象
            ps = connection.prepareStatement(SQL_SHOW_TABLE_STATUS);
            // 2. 执行查询，获取结果集
            tableResult = ps.executeQuery();

            // 3. 遍历结果集
            while (tableResult.next()) {
                // 获取表名（Name字段）
                String tableName = tableResult.getString("Name");
                // 获取表注释（Comment字段）
                String comment = tableResult.getString("Comment");

                // 使用日志输出表信息（使用占位符避免字符串拼接）
                logger.info("表名：{}，备注：{}", tableName, comment);
                String beanName = tableName;
                //如果需要忽略前缀 需要截取掉
                if (Constants.IGNORE_TABLE_PREFIX) {
                    beanName = tableName.substring(tableName.indexOf("_") + 1);
                }
                //转驼峰
                beanName = processFiled(beanName, true);
                logger.info("表名：{}，beanName：{}", tableName, beanName);
                TableInfo tableInfo = new TableInfo();
                tableInfo.setTableName(tableName);
                tableInfo.setBeanName(beanName);
                tableInfo.setComment(comment);
                tableInfo.setBeanParamName(beanName + Constants.SUFFIX_BEAN_QUERY);
                //logger.info("表名：{}，beanName：{}，beanParamName：{}", tableName, beanName, tableInfo.getBeanParamName());
                readTableFields(tableInfo);
                // logger.info("表: {}", JsonUtils.convertToJson(tableInfo));
                //  logger.info("字段: {}", JsonUtils.convertToJson(tableInfo));
                //读取索引
                getKeyIndexInfo(tableInfo);
                logger.info("tableInfo: {}", JsonUtils.convertToJson(tableInfo));
                tableInfoList.add(tableInfo);
            }
        } catch (Exception e) {
            // 捕获并记录查询过程中的异常
            logger.error("获取表失败: {}", e.getMessage());
        } finally {
            // 资源释放块（确保数据库资源被正确关闭）

            // 1. 关闭结果集
            if (tableResult != null) {
                try {
                    tableResult.close();
                } catch (SQLException e) {
                    logger.error("关闭结果集时出错: {}", e.getMessage());
                }
            }

            // 2. 关闭语句对象
            if (ps != null) {
                try {
                    ps.close();
                } catch (SQLException e) {
                    logger.error("关闭语句对象时出错: {}", e.getMessage());
                }
            }

            // 3. 关闭数据库连接
            if (connection != null) {
                try {
                    connection.close();
                } catch (SQLException e) {
                    logger.error("关闭数据库连接时出错: {}", e.getMessage());
                }
            }
        }
        return tableInfoList;
    }

    /**
     * 读取表字段信息
     *
     * @param tableInfo
     * @return
     */
    private static void readTableFields(TableInfo tableInfo) {
        PreparedStatement ps = null;       // SQL语句对象
        ResultSet fieldResult = null;      // 结果集对象

        List<FiledInfo> filedInfoList = new ArrayList<>();
        try {
            // 1. 创建预处理语句对象
            ps = connection.prepareStatement(String.format(SQL_SHOW_TABLE_FIELDS, tableInfo.getTableName()));
            // 2. 执行查询，获取结果集
            fieldResult = ps.executeQuery();

            Boolean haveDateTime = false;
            Boolean haveDate = false;
            Boolean haveBigDecimal = false;
            // 3. 遍历结果集
            while (fieldResult.next()) {

                String field = fieldResult.getString("field");
                String type = fieldResult.getString("type");
                String extra = fieldResult.getString("extra");
                String comment = fieldResult.getString("comment");
                if (type.indexOf("(") > 0) {
                    type = type.substring(0, type.indexOf("("));
                }
                String propertyName = processFiled(field, false);
                FiledInfo filedInfo = new FiledInfo();
                filedInfoList.add(filedInfo);
                tableInfo.setFiledList(filedInfoList);
                filedInfo.setFiledName(field);
                filedInfo.setComment(comment);
                filedInfo.setSqlType(type);
                filedInfo.setAutoIncrement("auto_increment".equalsIgnoreCase(extra) ? true : false);
                filedInfo.setPropertyName(propertyName);
                filedInfo.setJavaType(processJavaType(type));
                logger.info("字段: {}, 类型: {},属性名称: {}, 额外信息: {}, 注释: {}", field, type, propertyName, extra, comment);
                logger.info("JavaType: {}", filedInfo.getJavaType());

                if (ArrayUtils.contains(Constants.SQL_DATE_TIME_TYPES, type)) {
                    haveDateTime = true;
                }
                if (ArrayUtils.contains(Constants.SQL_DATE_TYPES, type)) {
                    haveDate = true;
                }
                if (ArrayUtils.contains(Constants.SQL_DECIMAL_TYPES, type)) {
                    haveBigDecimal = true;
                }

            }
            tableInfo.setHaveDate(haveDate);
            tableInfo.setHaveDateTime(haveDateTime);
            tableInfo.setHaveBigDecimal(haveBigDecimal);
            tableInfo.setFiledList(filedInfoList);
        } catch (Exception e) {
            // 捕获并记录查询过程中的异常
            logger.error("读取字段失败: {}", e.getMessage());
        } finally {
            // 资源释放块（确保数据库资源被正确关闭）

            // 1. 关闭结果集
            if (fieldResult != null) {
                try {
                    fieldResult.close();
                } catch (SQLException e) {
                    logger.error("关闭结果集时出错: {}", e.getMessage());
                }
            }

            // 2. 关闭语句对象
            if (ps != null) {
                try {
                    ps.close();
                } catch (SQLException e) {
                    logger.error("关闭语句对象时出错: {}", e.getMessage());
                }
            }


        }

    }

    /**
     * 获取表的唯一索引信息，并填充到 TableInfo 的 keyIndexMap 中
     *
     * @param tableInfo 表信息对象，包含表名、字段列表等元数据
     * @return 始终返回空列表（代码设计问题，返回值未被使用）
     */
    private static List<FiledInfo> getKeyIndexInfo(TableInfo tableInfo) {
        PreparedStatement ps = null;       // SQL语句对象，用于执行 SHOW INDEX 查询
        ResultSet fieldResult = null;      // 结果集对象，用于接收索引查询结果

        List<FiledInfo> filedInfoList = new ArrayList<>();  // 初始化空列表（问题点：后续未填充数据）
        try {
            // 创建临时映射表：字段名 -> FiledInfo 对象（用于快速通过列名查找字段信息）
            Map<String, FiledInfo> tempMap = new HashMap<>();
            for (FiledInfo filedInfo : tableInfo.getFiledList()) {
                tempMap.put(filedInfo.getFiledName(), filedInfo);
            }

            // 1. 准备 SQL 语句（SHOW INDEX FROM 表名）
            ps = connection.prepareStatement(
                    String.format(SQL_SHOW_TABLE_INDEX, tableInfo.getTableName())
            );

            // 2. 执行查询，获取索引元数据
            fieldResult = ps.executeQuery();

            // 3. 遍历索引结果集
            while (fieldResult.next()) {
                // 解析索引元数据
                String keyName = fieldResult.getString("key_name");      // 索引名称（如 PRIMARY）
                Integer nonUnique = fieldResult.getInt("non_unique");    // 是否允许重复值（0=唯一，1=非唯一）
                String columnName = fieldResult.getString("column_name");// 索引关联的列名

                // 跳过非唯一索引（只处理主键和唯一约束）
                if (nonUnique == 1) {
                    continue;  // 终止本次循环，进入下一次迭代
                }

                // 获取或创建当前索引的字段列表
                List<FiledInfo> KeyFieldList = tableInfo.getKeyIndexMap().get(keyName);
                if (KeyFieldList == null) {
                    KeyFieldList = new ArrayList<>();
                    tableInfo.getKeyIndexMap().put(keyName, KeyFieldList);
                }

                // 通过列名找到 FiledInfo 对象，添加到索引字段列表
                KeyFieldList.add(tempMap.get(columnName));
            }
        } catch (Exception e) {
            // 捕获并记录异常（如SQL语法错误、连接失效等）
            logger.error("读取索引失败: {}", e.getMessage());
        } finally {
            // 资源释放（确保数据库连接资源被回收）

            // 1. 关闭结果集
            if (fieldResult != null) {
                try {
                    fieldResult.close();
                } catch (SQLException e) {
                    logger.error("关闭结果集时出错: {}", e.getMessage());
                }
            }

            // 2. 关闭语句对象
            if (ps != null) {
                try {
                    ps.close();
                } catch (SQLException e) {
                    logger.error("关闭语句对象时出错: {}", e.getMessage());
                }
            }
        }
        return filedInfoList;  // 返回空列表（设计缺陷：方法应改为void，或实际填充数据）
    }

    /**
     * 处理字段 转为驼峰格式
     *
     * @param field
     * @param UpperCaseFirstLetter
     * @return
     */
    private static String processFiled(String field, boolean UpperCaseFirstLetter) {
        StringBuffer sb = new StringBuffer();
        String[] fields = field.split("_");
        sb.append(UpperCaseFirstLetter ? StringUtils.upperCaseFirstLetter(fields[0]) : fields[0]);
        for (int i = 1, len = fields.length; i < len; i++) {
            sb.append(StringUtils.upperCaseFirstLetter(fields[i]));
        }
        return sb.toString();
    }


    /**
     * 将sql类型转为Java类型
     *
     * @param sqlType
     * @return
     */
    private static String processJavaType(String sqlType) {
        if (ArrayUtils.contains(Constants.SQL_STRING_TYPES, sqlType)) {
            return "String";
        } else if (ArrayUtils.contains(Constants.SQL_INTEGER_TYPES, sqlType)) {
            return "Integer";
        } else if (ArrayUtils.contains(Constants.SQL_LONG_TYPES, sqlType)) {
            return "Long";
        } else if (ArrayUtils.contains(Constants.SQL_DATE_TIME_TYPES, sqlType) || ArrayUtils.contains(Constants.SQL_DATE_TYPES, sqlType)) {
            return "Date";
        } else if (ArrayUtils.contains(Constants.SQL_DECIMAL_TYPES, sqlType)) {
            return "BigDecimal";
        } else {
            throw new RuntimeException("不支持的数据类型：" + sqlType);
        }

    }


}